/*!
 * \file build_sparse_constraint_visitor.cc
 *
 * \brief Implementation of the BuildSymTableVisitor
 *
 * \date Started: 6/8/2010
 * \date Last Modified: 6/28/2010
 * \authors Alan LaMielle Nicholas Jeanette
 *
 * Copyright (c) 2010, Colorado State University <br>
 * All rights reserved. <br>
 * See ../../COPYING for details. <br>
 */

#include <iegenlib/iegenlib.h>

#include <iostream>
#include <algorithm>
#include <sstream>

namespace iegenlib{

BuildSparseConstraints::BuildSparseConstraints(SymbolTable* SymT,
              set<SREquality> equals, list<TupleType> tups):
              STEList(tups)
{
   ST = SymT;
   IeqConjSet = set<SRInequality>();
   EqConjSet=equals;
   this->Conjunct = NULL;
}

//void BuildSparseConstraints::UnionOut(Union* node){
//visitUnion
//Just do a union operation
//}

void BuildSparseConstraints::FuncExpOut(FuncExp* node)
{

   int coeff = node->getCoeff();
//   string name = funcExpString(node);
   string name = node->getName();
   list<NormExp*> params = node->getArgs();
   list<ExpVec> exp;

   // iterate through the list of NormExp
   // find the NormExp in the map and add it to the list of ExpVecs
   // need the list to look up the STE for the function
   for(list<NormExp*>::iterator itr = params.begin();
         itr != params.end();
            ++itr)
   {
      map<const Node*, ExpVec>::iterator tmp = mNodeToExpVec.find(*itr);
      if(tmp != mNodeToExpVec.end())
      {
         exp.push_back(tmp->second);
      }
   }

   // add the node to the map
   ExpVec vec = ST->constructExpVec(name,exp,coeff);
   mNodeToExpVec.insert(pair<Node*,ExpVec>(node,vec));
}

void BuildSparseConstraints::VarExpOut(VarExp* node)
{
   int coeff = node->getCoeff();
   if(node->getId() != "")
   {
      ExpVec vec = ST->constructExpVec(node->getId(),coeff);
      mNodeToExpVec.insert(pair<Node*,ExpVec>(node,vec));
   }else{
      ExpVec vec = ST->constructExpVec(coeff);
      mNodeToExpVec.insert(pair<Node*,ExpVec>(node,vec));
   }

}

void BuildSparseConstraints::NormExpOut(NormExp* node)
{
   ExpVec vec = ST->constructExpVec();
   if(node->getTerms().size() != 0){
      list<Expression*> terms = node->getTerms();
      // grab the first expression from the list of terms in order to grab an
      // ExpVec so we can reinsert it with a new key
      map<const Node*,ExpVec>::iterator temp_map = mNodeToExpVec.find((*terms.begin()));
      vec = temp_map->second;
      mNodeToExpVec.erase(temp_map);

      // iterate thought the list of Expressions find the Expression in the map
      // add it with the ExpVec vec
      for(list<Expression*>::iterator itr = ++terms.begin();
            itr != terms.end();
               ++itr)
      {
         map<const Node*, ExpVec>::iterator tmp = mNodeToExpVec.find(*itr);
         if(tmp != mNodeToExpVec.end())
         {
           vec = vec + (tmp->second);
            mNodeToExpVec.erase(tmp);
         }
      }
   }
   vec.add_const(node->getConst());

   // add this node to the map
   mNodeToExpVec.insert(pair<Node*,ExpVec>(node, vec));
}

//simple toString that just prints out the node type and the expVec associated
//to the node held in the map mNodeToExpVec
std::string BuildSparseConstraints::toString()
{
   std::string tmp;
   std::string var ("VarExp");
   std::string norm ("NormExp");
   std::string func ("FuncExp");

   set<string> print;
   for(map<const Node*,ExpVec>::iterator itr = mNodeToExpVec.begin();
         itr != mNodeToExpVec.end();
            ++itr)
   {

         print.insert((itr->second).toString(ST));

   }

  for(set<string>::iterator itr = print.begin();
         itr != print.end();
            ++itr)
   {

         tmp += "(" +(*itr) + ")";

   }
   return tmp;
}

std::string BuildSparseConstraints::funcExpString(FuncExp* node)
{
   std::string funcString(node->getName());
   list<NormExp*> params = node->getArgs();
   funcString+="(";
   //iterate though the list of params grabbing the expvecs that map to the
   //function params form here we grab the string representation of the list and
   //concatenate it to the function name
   for(list<NormExp*>::iterator itr = params.begin();
         itr != params.end();
            ++itr)
   {
      map<const Node*, ExpVec>::iterator tmp = mNodeToExpVec.find(*itr);
      if(tmp != mNodeToExpVec.end()){
         funcString+= (tmp->second).toString(ST) + ",";
      }
   }
   funcString = funcString.substr(0,funcString.length()-1);
   funcString+=")";
   return funcString;
}

void BuildSparseConstraints::ConjunctionOut(Conjunction* node)
{
      Conjunct=new SRConjunction(EqConjSet, IeqConjSet, STEList);
}

void BuildSparseConstraints::EqualityOut(Equality* node)
{
   const NormExp* norm = node->getExpression();
   ExpVec vec = mNodeToExpVec.find(norm)->second;
   vec.normalizeEquality();
   EqConjSet.insert(SREquality(vec));
}

void BuildSparseConstraints::InequalityOut(Inequality* node)
{
   const NormExp* norm = node->getExpression();
   ExpVec vec = mNodeToExpVec.find(norm)->second;
   IeqConjSet.insert(SRInequality(vec));
}

SRConjunction BuildSparseConstraints::getConjunction()
{
   SRConjunction conjunct=*(this->Conjunct);
   delete this->Conjunct;
   return conjunct;
}

void BuildSparseConstraints::PresSetIn(PresSet* node)
{
   throw StringException("can not use visitor with set of multiple conjunctions");
}

void BuildSparseConstraints::PresRelationIn(PresRelation* node)
{
   throw StringException("can not use visitor with relation of multiple conjunctions");
}

}//end namespace iegenlib
